﻿package rob.core{
	import flash.display.Sprite;
	import rob.data.*;
	import rob.maths.*;
	import rob.entity.*;
	import flash.geom.Point;
	
	public class World3d{
		public var time:uint;
		private static var _instance:World3d = undefined;
		private var _scene:Sprite;
		private var _camera:Camera3d, _initHeight:Number;
		private var _origin:Vert3d;
		private var _buildings:Array;
		private var _structures:Array;
		private var _ground:Ground;
		private var _bots:Array;
		private var _cars:Array;
		private var _bounds:Sprite;
		private var _botId:uint, _marker:Sprite;
		
		public function World3d():void{
			_origin = new Vert3d(0,0,0);
			_buildings = [];
			_structures = [];
			_cars = [];
			_ground = new Ground();
			_bots = [];
			_botId = 0;
		}
		
		public function initialise():void{
			_initHeight = _camera.position.y;
			//rotateBy(-Math.PI/8);
			time = 0;
			// useful marker mc for testing
			_marker = new pnt();
			_scene.addChild(_marker);
			_marker.scaleX = _marker.scaleY = 0.3;
			_marker.visible = false;
			// update all structures for first time
			for each(var s:Structure in _structures)
				s.update(_camera,_scene);
		}
		
		public function update():void{
			for each(var b:Bot in _bots) b.update(_camera,_bounds);
			for each(var c:Car in _cars) c.update(_camera,_bounds);
			_render();
			_ground.update();
			time++;
		}
		
		// main render etc. function
		// includes collision detection
		private function _render():void{
			var m:Mesh3d, cn:Vect3d = _camera.basis.k, p:Poly3d, sd:Side;
			var cp:Vert3d = _camera.position;
			var i:uint, L:uint, rel:Vect3d, dp:Number;
			var gp1:Point, gp2:Point, gp3:Point;
			// array of visible faces
			var aF:Array = [], aB:Array;
			// array of visible structures, array of visible faces in structure
			var aS:Array = [], aSF:Array;
			
			// *NEW* loop over structures
			for each(s in _structures){
				// test visibility
				if(!s.baseHitTest.hitTestObject(_bounds)) s.setVisible(false);
				else s.setVisible(true);
				
				for each(var c:Bot in _bots){
					// collision detection (necessary even if not visible)
					if(s.baseHitTest.hitTestObject(c.anim)){
						gp1 = c.gCollide1;	gp2 = c.gCollide2;	gp3 = c.gCollide3;
						if(s.baseHitTest.hitTestPoint(gp1.x,gp1.y,true) 
								|| s.baseHitTest.hitTestPoint(gp2.x,gp2.y,true)
								|| s.baseHitTest.hitTestPoint(gp3.x,gp3.y,true))
							c.changeDirBy(Math.PI);
					}

				}
				// skip if not visible
				if(!s.visible) continue;
				
				s.update(_camera,_scene);
				aSF = [];
				// find visible faces and render
				// check if side visible, if so push onto aSF
				L = s.sides.length;
				for(i = 0; i < L; i++){
					sd = s.sides[i];
					rel = VectorMaths.sub(sd.pnt3d,cp);
					dp = VectorMaths.dp(rel,sd.normal);
					if(dp <= 0){
						sd.visible = true;
						sd.render();
						sd.depth = -VectorMaths.lenTaxi(rel);
						aSF.push(sd);
					}
					else if(sd.visible) sd.visible = false;
				}
				// store visible sides, used for bot ordering
				s.visSides = aSF;
				
				// sort the sides of structure (inside mc)
				// don't both sorting 4 sides objects
				if(L > 4){
					aSF.sortOn("depth",Array.NUMERIC);
					L = aSF.length;
					for(i = 0; i < L; i++) s.gfx.setChildIndex(aSF[i],1+i);
				}
			}
			
			// order structures
			aS = _structures.slice();
			L = aS.length;
			aS.sortOn("order",Array.NUMERIC);
			for(i = 0; i < L; i++) _scene.setChildIndex(aS[i].gfx,1+i);
			
			// order bots/cars [TODO: improve]
			for each(v in _cars) _scene.setChildIndex(v.anim,2);
			for each(c in _bots) _scene.setChildIndex(c.anim,2);
			
		
			/*
			// loop over buildings
			for each(b in _buildings){
				
				// if building out of bounds then set as invisible
				if(! b.base.hitTestObject(_bounds)) b.setVisible(false);
				else b.setVisible(true);
				
				// check bots collisions
				for each(var c:Bot in _bots){
					// collision detection
					if(b.base.hitTestObject(c.anim)){
						gp1 = c.gCollide1;	gp2 = c.gCollide2;	gp3 = c.gCollide3;
						if(b.base.hitTestPoint(gp1.x,gp1.y,true) 
									|| b.base.hitTestPoint(gp2.x,gp2.y,true)
									|| b.base.hitTestPoint(gp3.x,gp3.y,true)){
							c.changeDirBy(Math.PI);
						}
					}
				}
			
				// no need to project invisible meshes
				if(!b.visible) continue;
				
				// project mesh and determine visible polygons
				m = b.mesh;
				// project points
				m.project(_camera);
				L = m.aPolygon.length;
				// check if poly visible, if so push onto aF
				for(i = 0; i < L; i++){
					p = m.aPolygon[i];
					rel = VectorMaths.sub(p.p0,cp);
					if(VectorMaths.dp(rel,p.normal)<=0){
						p.visible = true;
						aF.push(p);
					}
					else if(p.visible) p.visible = false;
				}
			}
			// order & render build polys
			L = aF.length;
			aF.sortOn("zdepth",Array.NUMERIC);
			for(i = 0; i < L; i++){
				aF[i].render();
				_scene.setChildIndex(aF[i],1+i);
			}
			*/
			
	

		}
		
		
		// TRANSFORM
			public function rotateBy(dtheta:Number):void{
				var deg:Number = dtheta * (180/ Math.PI);
				_camera.rotateBy(dtheta);
				_ground.container.rotation -= deg;
				for each(var b:Bot in _bots) b.rotateBy(dtheta);
				for each(var c:Car in _cars) c.rotateBy(dtheta);
				for each(var s:Structure in _structures) s.rotateBy(-deg);
			}
			
			public function zoomBy(dz:Number):void{
				if(_camera.position.y + dz > 400){
					_camera.moveRel(0,0,dz);
					var scf:Number = (_initHeight/_camera.position.y);
					_ground.container.scaleX = _ground.container.scaleY = scf;
					for each(var b:Bot in _bots) b.scaleBy(scf);
					for each(var c:Car in _cars) c.scaleBy(scf);
					for each(var s:Structure in _structures) s.setDimensions(_camera);
				}
			}
			
			public function camMoveRel(dx:Number,dy:Number,dz:Number):void{
				_camera.moveRel(dx,dy,dz);
			}
			
		// ADD ENTITIES
			public function addBuilding(b:Building):void{
				_buildings.push(b);
			}
			
			public function addStructure(s:Structure):void{
				_structures.push(s);
			}
			
			public function addFloor(f:Floor):void{
				_ground.addFloor(f);
			}
						
			public function addBot(b:Bot):void{
				_bots.push(b);
			}
		
			public function addCar(c:Car):void{
				_cars.push(c);
			}
		
		public static function get instance():World3d{
			if(_instance == null) _instance = new World3d();
			return _instance;
		}
		
		public function getNewBotId():uint{
			_botId++;
			return _botId;
		}
		
		public function get origin():Vert3d{return _origin;}
		public function get buildings():Array{return _buildings;}
		public function get structures():Array{return _structures;}
		public function get bots():Array{return _bots;}
		public function get cars():Array{return _cars;}
		public function get ground():Ground{return _ground;}
		public function get scene():Sprite{return _scene;}
		public function get camera():Camera3d{return _camera;}
		
		public function set bounds(s:Sprite):void{_bounds = s;}
		public function set scene(s:Sprite):void{_scene = s;}
		public function set camera(c:Camera3d):void{
			_camera = c;
			_ground.camera = c;
		}
	}
}